/**
 *  Version:     @(#)aio_socket.c    0.2.12 28/09/2006
 *
 *  Authors:     Hailong Xia <hlxxxx@gmail.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty
 *  of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 *  the GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General
 *  Public License along with this library; if not, write to the
 *  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *  Boston, MA 02111-1307  USA
 */

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h> 
#include <assert.h>

#ifdef _WIN32
# include <winsock2.h>
# include <ws2tcpip.h>
#else
# include <unistd.h>
# include <sys/socket.h>
# include <netinet/tcp.h>
# include <arpa/inet.h>
# include <netinet/in.h>
#endif

#include "aio_log.h"
#include "aio_socket.h"

#ifdef _WIN32
#define SIO_KEEPALIVE_VALS _WSAIOW(IOC_VENDOR, 4)
#endif

static int is_ipv4_addr(const char* addr);
static int is_ipv6_addr(const char* addr);

#ifdef _WIN32
static const char * inet_ntop4(const unsigned char *src, char *dst, size_t size);
static int inet_pton4(const char *src, unsigned char *dst);
#ifdef _AIO_IPV6
static const char * inet_ntop6(unsigned char const *src, char *dst, size_t size);
static int inet_pton6(const char *src, unsigned char *dst);
#endif /* _AIO_IPV6 */
#endif /* _WIN32 */

int 
is_ip_addr(int af, const char *addr)
{
        if (!is_ipv4_addr(addr)) {
#ifdef _AIO_IPV6
                if (!is_ipv6_addr(addr)) {
                        return -1;
                }
#else 
                return -1;
#endif
        }
        return 0;
}

static int 
is_ipv4_addr(const char* addr)
{
        unsigned int p1, p2, p3, p4;
        int count = 0, result;

        if (!addr)
                return 0;

        result = sscanf(addr, "%u.%u.%u.%u%n", 
                        &p1, &p2, &p3, &p4, &count);

        if ((result == 4) 
            && (p1 <= 255)
            && (p2 <= 255)
            && (p3 <= 255)
            && (p4 <= 255)
            && (count == strlen(addr))) {
                return 1;
        }
        else {
                return 0;
        }
}

#ifdef _AIO_IPV6

/** 
 * IpV6 address sample:
 * 3ffe:3201:1401:1:280:c8ff:fe4d:db39
 * 1080:0:0:0:8:800:200C:417A
 * 1080::8:800:200C:417A
 * 2000:0:0:0:0:0:0:1
 * 2000::1
 * 0:0:0:0:0:0:10.0.0.1 (not check success)
 * ::10.0.0.1 (not check success)
 */

static int
is_ipv6_addr(const char* addr)
{
        int i, n = 0;

        if (!addr) 
                return 0;

        if (!isxdigit(addr[0]) && addr[0] != ':') 
                return 0;

        n = 0;

        for (i = 0; i < (int)strlen(addr); i++) {
                if (addr[i] == ':') {
                        n++;
                        continue;
                }

                if (!isxdigit(addr[i])) 
                        return 0;
        }

        if (n > 7)
                return 0;

        switch(strlen(addr)) {
        case 1:
                return 0;
        case 2:
                return (addr[1] == ':' || 
                        addr[0] == ':');
        case 3:
                return (addr[2] == ':' ||
                        addr[1] == ':' ||
                        addr[0] == ':');
        case 4: 
                return (addr[3] == ':' ||
                        addr[2] == ':' ||
                        addr[1] == ':' ||
                        addr[0] == ':');
        default:
                return (addr[4] == ':' ||
                        addr[3] == ':' ||
                        addr[2] == ':' ||
                        addr[1] == ':' ||
                        addr[0] == ':');
        }
}

#endif /* _AIO_IPV6 */

#ifdef _WIN32

const char *
inet_ntop(int af, void const *src, char *dst, size_t size)
{
        switch (af) {
        case AF_INET:
                return inet_ntop4(src, dst, size);
#ifdef _AIO_IPV6
        case AF_INET6:
                return inet_ntop6(src, dst, size);
#endif
        default:
                return NULL;
        }
}

static const char *
inet_ntop4(const unsigned char *src, char *dst, size_t size)
{
        static const char fmt[] = "%u.%u.%u.%u";
        char tmp[sizeof "255.255.255.255"];

        if (_snprintf(tmp, sizeof tmp, fmt, 
                src[0], src[1], src[2], src[3]) >= (int)size) {
                return NULL;
        }

        return strcpy(dst, tmp);
}

#ifdef _AIO_IPV6

static const char *
inet_ntop6(unsigned char const *src, char *dst, size_t size)
{
        /**
         * Note that int32_t and int16_t need only be "at least" large enough
         * to contain a value of the specified size.  On some systems, like
         * Crays, there is no such thing as an integer variable with 16 bits.
         * Keep this in mind if you think this function should have been coded
         * to use pointer overlays.  All the world's not a VAX.
         */
        char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
        struct { int base, len; } best = { -1 , 0 }, cur = { -1, 0 };
        unsigned int words[8];
        int i;

        /**
         * Preprocess:
         *      Copy the input (bytewise) array into a wordwise array.
         *      Find the longest run of 0x00's in src[] for :: shorthanding.
         */
        for (i = 0; i < 16; i += 2)
                words[i / 2] = (src[i] << 8) | (src[i + 1]);
        best.base = -1;
        cur.base = -1;
        for (i = 0; i < 8; i++) {
                if (words[i] == 0) {
                        if (cur.base == -1)
                                cur.base = i, cur.len = 1;
                        else
                                cur.len++;
                } else {
                        if (cur.base != -1) {
                                if (best.base == -1 || cur.len > best.len)
                                        best = cur;
                                cur.base = -1;
                        }
                }
        }
        if (cur.base != -1) {
                if (best.base == -1 || cur.len > best.len)
                        best = cur;
        }
        if (best.base != -1 && best.len < 2)
                best.base = -1;

        /**
         * Format the result.
         */
        tp = tmp;
        for (i = 0; i < 8; i++) {
                /* Are we inside the best run of 0x00's? */
                if (best.base != -1 && i >= best.base &&
                    i < (best.base + best.len)) {
                        if (i == best.base)
                                *tp++ = ':';
                        continue;
                }
                /* Are we following an initial run of 0x00s or any real hex? */
                if (i != 0)
                        *tp++ = ':';
                /* Is this address an encapsulated IPv4? */
                if (i == 6 && best.base == 0 &&
                    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
                        if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
                                return NULL;
                        tp += strlen(tp);
                        break;
                }
                tp += sprintf(tp, "%x", words[i]);
        }
        /* Was it a trailing run of 0x00's? */
        if (best.base != -1 && (best.base + best.len) == 8)
                *tp++ = ':';
        *tp++ = '\0';

        /**
         * Check for overflow, copy, and we're done.
         */
        if ((size_t)(tp - tmp) >= size) {
                return NULL;
        }

        return strcpy(dst, tmp);
}

#endif /* _AIO_IPV6 */

int
inet_pton(int af, const char * src, void * dst)
{
        switch (af) {
        case AF_INET:
                return (inet_pton4(src, dst));
#ifdef _AIO_IPV6
        case AF_INET6:
                return (inet_pton6(src, dst));
#endif
        default:
                return -1;
        }
}

static int
inet_pton4(const char *src, unsigned char *dst)
{
        int saw_digit, octets, ch;
        unsigned char tmp[4], *tp;

        saw_digit = 0;
        octets = 0;
        *(tp = tmp) = 0;
        while ((ch = *src++) != '\0') {
                if ('0' <= ch && ch <= '9') {
                        unsigned octet = *tp * 10 + ch - '0';

                        if (saw_digit && *tp == 0)
                                return 0;
                        if (octet > 255)
                                return 0;
                        *tp = octet;
                        if (!saw_digit) {
                                if (++octets > 4)
                                        return 0;
                                saw_digit = 1;
                        }
                } else if (ch == '.' && saw_digit) {
                        if (octets == 4)
                                return 0;
                        *++tp = 0;
                        saw_digit = 0;
                } else
                        return 0;
        }
        if (octets < 4)
                return 0;
        memcpy(dst, tmp, 4);
        return 1;
}

#ifdef _AIO_IPV6

static int
inet_pton6(const char *src, unsigned char *dst)
{
        unsigned char tmp[16], *tp, *endp, *colonp;
        const char *curtok;
        int ch, saw_xdigit;
        unsigned val;

        memset((tp = tmp), '\0', sizeof tmp);
        endp = tp + sizeof tmp;
        colonp = NULL;
        /* Leading :: requires some special handling. */
        if (*src == ':')
                if (*++src != ':')
                        return 0;
        curtok = src;
        saw_xdigit = 0;
        val = 0;
        while ((ch = *src++) != '\0') {
                if (ch == ':') {
                        curtok = src;
                        if (!saw_xdigit) {
                                if (colonp)
                                        return 0;
                                colonp = tp;
                                continue;
                        } else if (*src == '\0') {
                                return 0;
                        }
                        if (tp + 2 > endp)
                                return 0;
                        *tp++ = (unsigned char) (val >> 8) & 0xff;
                        *tp++ = (unsigned char) val & 0xff;
                        saw_xdigit = 0;
                        val = 0;
                        continue;
                }
                if (ch == '.' && ((tp + 4) <= endp) &&
                    inet_pton4(curtok, tp) > 0) {
                        tp += 4;
                        saw_xdigit = 0;
                        break;  /* '\0' was seen by inet_pton4(). */
                }

                if ('0' <= ch && ch <= '9')
                        ch = ch - '0';
                else if ('A' <= ch && ch <= 'F')
                        ch = ch - 'A' + 10;
                else if ('a' <= ch && ch <= 'f')
                        ch = ch - 'a' + 10;
                else
                        return 0;
                val <<= 4;
                val |= ch;
                if (val > 0xffff)
                        return 0;
                saw_xdigit = 1;
        }
        if (saw_xdigit) {
                if (tp + 2 > endp)
                        return 0;
                *tp++ = (unsigned char) (val >> 8) & 0xff;
                *tp++ = (unsigned char) val & 0xff;
        }
        if (colonp != NULL) {
                /**
                 * Since some memmove()'s erroneously fail to handle
                 * overlapping regions, we'll do the shift by hand.
                 */
                const int n = (int)(tp - colonp);
                int i;

                if (tp == endp)
                        return 0;
                for (i = 1; i <= n; i++) {
                        endp[- i] = colonp[n - i];
                        colonp[n - i] = 0;
                }
                tp = endp;
        }
        if (tp != endp)
                return 0;
        memcpy(dst, tmp, sizeof tmp);
        return 1;
}

#endif /* _AIO_IPV6 */
#endif /* _WIN32 */

int 
set_socket_blocking(int fd)
{
#ifdef _WIN32
        int mode = 0;
        if (ioctlsocket(fd, FIONBIO, (u_long FAR*) &mode) != 0) {
                return -1;
        }
#else
        int flags = fcntl(fd, F_GETFL, 0);
        if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) != 0) {
                return -1;
        }
#endif
        return 0;
}

int 
set_socket_nonblocking(int fd)
{
#ifdef _WIN32
        int mode = 1;
        if (ioctlsocket(fd, FIONBIO, (u_long FAR*) &mode) != 0) {
                return -1;
        }
#else
        int flags = fcntl(fd, F_GETFL, 0); 
        if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
                return -1;
        }
#endif
        return 0;
}

int
get_socket_ttl(int fd, int *ttl)
{
        int len = sizeof(int);

        return getsockopt(fd, IPPROTO_IP, IP_TTL, (char *)ttl, &len);
}

int
set_socket_ttl(int fd, int ttl)
{
        return setsockopt(fd, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(int));
}

int 
set_tcp_keepalive(int fd, int keepalive, 
        int keepidle, int keepintvl, int keepcnt)
{
#ifdef _WIN32
        struct {
                unsigned long onoff;
                unsigned long keepalivetime;
                unsigned long keepaliveinterval;
        } in_keepalive, out_keepalive;

        unsigned long in_len, out_len, return_bytes;
#endif

        if (fd <= 0) {
                log_warn("Invalid file descriper.\n");
                return -1;
        }

        if (keepalive == 0) /* turn off keepalive */
                return 0;

        if (keepidle <= 0) {
                log_warn("Invalid idle value: %d\n", keepidle);
                return -1;
        }

        if (keepintvl <= 0) {
                log_warn("Invalid interval value: %d\n", keepintvl);
                return -1;
        }

        if (keepcnt <= 0) {
                log_warn("Invalid count value: %d\n", keepcnt);
                return -1;
        }

#if defined (_WIN32)
        in_len = sizeof(in_keepalive);
        out_len = sizeof(out_keepalive);
        return_bytes = 0;

        memset(&in_keepalive, 0, sizeof(in_keepalive));
        memset(&out_keepalive, 0, sizeof(out_keepalive));

        in_keepalive.onoff = keepalive;
        in_keepalive.keepaliveinterval = keepintvl;
        in_keepalive.keepalivetime = keepidle;

        /* hl, keepcnt default is 3 times on windows system. */
        if (WSAIoctl((unsigned int)fd, SIO_KEEPALIVE_VALS, 
                (LPVOID)&in_keepalive, in_len, (LPVOID)&out_keepalive, 
                out_len, &return_bytes, NULL, NULL) == -1) {
                log_warn("Set TCP keepalive failed, errno: %d\n", errnum);
                return -1;
        }
#elif defined (__linux__)
        if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE,
                (char*)&keepalive, sizeof(keepalive)) < 0) {
                log_warn("Set TCP keepalive failed, errno: %d\n", errnum);
                return -1;
        }

        if (setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,
                (char *)&keepidle, sizeof(keepidle)) < 0) {
                log_warn("Set TCP keepidle failed, errno: %d\n", errnum);
                return -1;
        }

        if (setsockopt(fd, SOL_TCP, TCP_KEEPINTVL,
                (char *)&keepintvl, sizeof(keepintvl)) < 0) {
                log_warn("Set TCP keepintval failed, errno: %d\n", errnum);
                return -1;
        }

        if (setsockopt(fd, SOL_TCP, TCP_KEEPCNT,
                (char *)&keepcnt, sizeof(keepcnt)) < 0) {
                log_warn("Set TCP keepcount failed, errno: %d\n", errnum);
                return -1;
        }
#endif
        return 0;
}

int
create_socket(int servicetype, int prototype, int nonblocking)
{
        int fd;

        fd = (int)socket(AF_INET, servicetype, prototype);
        if (fd == -1) {
                log_err("Create socket failed, errno: %d\n", errnum);
                return -1;       
        }

        if (nonblocking) {
                if (set_socket_nonblocking(fd) < 0) {
                        log_warn("Set socket nonblocking failed, errno: %d\n", errnum);
                        closesocket(fd);
                        return -1;
                }
        }
        return fd;
}

int
bind_socket(int fd, unsigned long ip, unsigned short port, int reuse)
{
        struct sockaddr_in addr;

        if (reuse) {
                if (setsockopt(fd, SOL_SOCKET,
                        SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
                        log_warn("Set socket options failed, errno: %d\n", errnum);
                        return -1;
                }
        }

        addr.sin_family = AF_INET;
        if (ip == 0) {
                addr.sin_addr.s_addr = INADDR_ANY;
        } else {
                addr.sin_addr.s_addr = ip;
        }
        addr.sin_port = htons(port);

        if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
                log_err("Bind socket addr failed, errno: %d\n", errnum);
                return -1;
        }

        return 0;
}

int 
tcp_accept(int fd, unsigned long *frmip,
        unsigned short *frmport, int blocking)
{
        int connfd, len;
        struct sockaddr_in addr;

        len = sizeof(struct sockaddr_in);

        connfd = (int)accept(fd, (struct sockaddr *)&addr, &len);
        if (connfd < 0) {
#ifdef _WIN32
                if (!(errnum == EWOULDBLOCK 
                        || errnum == WSAEINPROGRESS || errnum == EINTR))
#else
                if (!(errnum == EWOULDBLOCK || errnum == EINTR))
#endif
                {
                        return -1;
                }
                return 0;
        }

        *frmip = addr.sin_addr.s_addr;
        *frmport = addr.sin_port;

        if (!blocking) {
                if (set_socket_nonblocking(connfd) < 0) {
                        log_warn("Set socket nonblocking failed, errno: %d\n", errnum);
                        closesocket(connfd);
                        return -1;
                }
        } 

        return connfd;
}

int 
tcp_connect(int fd, unsigned long ip, unsigned short port)
{ 
        int ret;
        struct sockaddr_in addr;

        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = ip;
        addr.sin_port = htons(port);

        ret = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
        if (ret < 0) {
#ifdef _WIN32
                if (!(errnum == WSAEWOULDBLOCK || errnum == WSAEINPROGRESS))
#else
                if (errnum != EINPROGRESS)
#endif
                {
                        log_err("Tcp connect failed, errno: %d\n", errnum);
                        return -1;
                }
                return 0;
        }

        return fd;
}

int 
tcp_recv(int fd, char *buf, int len)
{
        int n;

#ifdef _WIN32
        n = recv(fd, buf, len, 0);
#else
        n = read(fd, buf, len);
#endif
        if (n < 0) {
                if (errnum == EWOULDBLOCK) {
                        debugp("Tcp read wouldblock, errno: %d\n", errnum);
                        return 0;
                }
                debugp("Tcp read failed, errno: %d\n", errnum);
                return -1;
        }
        else if (n == 0) {
                debugp("Tcp connection was close by peer\n");
                return -1;
        }
        return n;
}

int 
tcp_send(int fd, const char *data, int len)
{
        int n;

#ifdef _WIN32
        n = send(fd, data, len, 0);
#else
        n = write(fd, data, len);
#endif
        if (n >= 0) {
                return n;
        }
                
        if (errnum == EWOULDBLOCK) {
                debugp("Tcp write wouldblock, errno: %d\n", errnum);
                return 0;
        }

        debugp("Tcp write failed, errno: %d\n", errnum);
        return -1;
}

int 
udp_connect(int fd, const char* ip, unsigned short port)
{
        struct sockaddr_in addr;

        memset(&addr, 0, sizeof(struct sockaddr_in));
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port = htons(port);

        if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
                debugp("Udp connect failed, errno: %d\n", errnum);
                return -1;
        }

        return 0;
}

#ifdef _AIO_USE_OPENSSL

int 
ssl_server_init(SSL_CTX **ctx, const char *cacrt,
        const char *srvcrt, const char *srvkey)
{
        assert (srvcrt);
        assert (srvkey);
        SSLeay_add_ssl_algorithms ();
        OpenSSL_add_all_algorithms();
        SSL_load_error_strings ();

        *ctx = SSL_CTX_new(TLSv1_server_method());
        if (*ctx == NULL) {
                log_err("Call SSL_CTX_new failed, errno: [%d]\n", errnum);
                return -1;
        }

        SSL_CTX_set_verify(*ctx, SSL_VERIFY_NONE, NULL); /* Don't authenticate client certification */
        if (cacrt != NULL) {
                SSL_CTX_load_verify_locations (*ctx, cacrt, NULL);
        }
		
        if (SSL_CTX_use_certificate_file(*ctx, srvcrt, SSL_FILETYPE_PEM) <= 0) {
                log_err("Call SSL_CTX_use_certificate_file failed, errno: %d\n", errnum);
                return -1;
        }

        if (SSL_CTX_use_PrivateKey_file(*ctx, srvkey, SSL_FILETYPE_PEM) <= 0) {
                log_err("Call SSL_CTX_use_PrivateKey_file() failed, errno: %d\n", errnum);
                return -1;
        }

        if (!SSL_CTX_check_private_key(*ctx)) {
                log_err("Call SSL_CTX_check_private_key failed, errno: %d\n", errnum);
                return -1;
        }

        SSL_CTX_set_cipher_list(*ctx, "RC4-MD5");
        return 0;
}

int
ssl_client_init(int fd, SSL_CTX **ctx, SSL **ssl)
{
        SSLeay_add_ssl_algorithms ();
        SSL_load_error_strings ();
        OpenSSL_add_ssl_algorithms ();

        *ctx = SSL_CTX_new(TLSv1_client_method());
        if (*ctx == NULL) {
                log_err("Call SSL_CTX_new failed, errno: %d\n", errnum);
                return -1;
        }

        /* Don't authenticate server certification */
        SSL_CTX_set_verify (*ctx, SSL_VERIFY_NONE, NULL);

        *ssl = ssl_alloc(*ctx, fd);
        if (*ssl == NULL) return -1;
        return 0;
}

SSL*
ssl_alloc(SSL_CTX *ctx, int fd)
{
        SSL *ssl;

        ssl = SSL_new(ctx);
        if (ssl == NULL) {
                log_err("Call SSL_new failed, errno: %d\n", errnum);
                return NULL;
        }

        if (!SSL_set_fd(ssl, fd)) {
                log_err("Call SSL_set_fd failed, errno: %d\n", errnum);
                return NULL;
        }
        return ssl;
}

int
ssl_accept(SSL *ssl)
{
        int ret, err;
        
        ret = SSL_accept(ssl);
        if (ret <= 0) {
                err = SSL_get_error(ssl, ret);
                switch (err) {
                case SSL_ERROR_NONE:
                case SSL_ERROR_WANT_READ:
                case SSL_ERROR_WANT_WRITE:
                        debugp("SSL accept wouldblock, errno: %d\n", errnum);
                        return 0;
                default:
                        log_err("SSL accept failed, errno: %d\n", errnum);
                        return -1;
                }
                return 0;
        }
        return ret;
}

int 
ssl_connect(SSL *ssl)
{
        int ret, err;

        ret = SSL_connect(ssl);
        if (ret == 1) {
                return ret;
        }
                
        err = SSL_get_error(ssl, ret);
        switch (err) {
        case SSL_ERROR_NONE:
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
                debugp("SSL connect would-block, errno: %d\n", errnum);
                return 0;
        default:
                log_err("SSL connect failed, errno: %d\n", errnum);
                return -1; 
        }
}

int 
ssl_recv(SSL *ssl, char *buf, int len)
{
        int n, err;

        if (SSL_get_state(ssl) != SSL_ST_OK) {
                return ssl_accept(ssl);
        }

        n = SSL_read(ssl, buf, len);
        if (n <= 0) {
                err = SSL_get_error(ssl, n);
                switch (err) {
                case SSL_ERROR_NONE:
                case SSL_ERROR_WANT_READ:
                case SSL_ERROR_WANT_WRITE:
                        debugp("SSL read wouldblock, errno: %d\n", errnum);
                        return 0;
                default:
                        debugp("SSL read failed, errno: %d\n", errnum);
                        return -1;
                }
        }
        return n;
}

int 
ssl_send(SSL *ssl, const char *data, int len)
{
        int n, err;

        n = SSL_write(ssl, data, len);
        if (n < 0) {
                err = SSL_get_error(ssl, n);
                switch(err) {
                case SSL_ERROR_NONE:
                case SSL_ERROR_WANT_READ:
                case SSL_ERROR_WANT_WRITE:
                        debugp("SSL write wouldblock, errno: %d\n", errnum);
                        return 0;
                default:
                        debugp("SSL write failed, errno: %d\n", errnum);
                        return -1;
                }
        }
        return n;
}

#endif /* _AIO_USE_OPENSSL */

